﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.Concurrent;
using System.Reflection;
using Framework.Reflection;
using Framework.Extensions;

namespace Framework.Reflection
{
    public static class FastObjectFactory
    {
        static ConcurrentDictionary<string, ConstructorDelegate> cache = new ConcurrentDictionary<string, ConstructorDelegate>();
        public static object Create(Type t)
        {
            return GetObjectFactory(t).Invoke();
        }

        public static object Create(Type t, object[] args)
        {
            return GetObjectFactory(t, args.Select(x => x.GetType()).ToArray()).Invoke(args);
        }

        static ConstructorDelegate GetObjectFactory(Type t)
        {
            return GetObjectFactory(t, Type.EmptyTypes);
        }

        static ConstructorDelegate GetObjectFactory(Type t, Type[] argsType)
        {
            var key = t.FullName + argsType.Select(x => x.FullName).ToString("");
            return cache.GetOrAdd(key, (newKey) => constructionDelegate(newKey, t, argsType));
        }

        static Func<string, Type, Type[], ConstructorDelegate> constructionDelegate = (newKey, t, argsType) =>
        {
            var ci = t.GetConstructor(argsType);
            if (ci == null)
            {
                ci = t.GetConstructor(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null, argsType, null);
            }
            if (ci == null) throw new ApplicationException("Unable to find constructor for type " + t.FullName);
            return DynamicReflectionHelper.CreateConstructor(ci);
        };

    }
}
